﻿using System;
using System.Collections.Generic;
using System.Linq;
using BusinessBingo.Data.DataLoadOptions;
using BusinessBingo.Data.Filter;
using BusinessBingo.Data.Interfaces;
using BusinessBingo.Model;
using BusinessBingo.Model.Interfaces;
using BusinessBingo.Queries;
using BusinessBingo.QueryHandlers.Base;
using BusinessBingo.QueryHandlers.Interfaces;
using BusinessBingo.QueryHandlers.Results;

namespace BusinessBingo.QueryHandlers
{
    public class GetGameQueryHandler : BaseQueryHandler, IGetGameQueryHandler
    {
        private ICheckIfBingoSetIsFinishedQueryHandler _checkIfBingoSetIsFinishedQueryHandler;
        private IGetGroupQueryHandler _getGroupQueryHandler;
        private IGameRepository _gameRepository;
        private IBingoSetRepository _bingoSetRepository;

        public GetGameQueryHandler(
            ICheckIfBingoSetIsFinishedQueryHandler checkIfBingoSetIsFinishedQueryHandler,
            IGetGroupQueryHandler getGroupQueryHandler,
            IGameRepository gameRepository,
            IBingoSetRepository bingoSetRepository,
            IValidationState validationState) : base(validationState)
        {
            this._checkIfBingoSetIsFinishedQueryHandler = checkIfBingoSetIsFinishedQueryHandler;
            this._getGroupQueryHandler = getGroupQueryHandler;
            this._gameRepository = gameRepository;
            this._bingoSetRepository = bingoSetRepository;
        }

        public GetGameQueryHandlerResult Execute(GetGameQuery getGameQuery)
        {
            GetGameQueryHandlerResult result = new GetGameQueryHandlerResult();

            if (getGameQuery == default(GetGameQuery))
                throw new ArgumentNullException("GetGameQuery");

            Game game;
            Group group;
            if(getGameQuery.ByGroupUrlAndGameNumber == true)
            {
                if (string.IsNullOrWhiteSpace(getGameQuery.GroupUrl))
                    this._validationState.AddError<GetGameQuery>(x => x.GroupUrl, ErrorTypes.Empty);

                if (getGameQuery.GameNumber == default(int))
                    this._validationState.AddError<GetGameQuery>(x => x.GameNumber, ErrorTypes.Empty);

                if (this._validationState.IsValid == false)
                    return result;

                GetGroupQuery getGroupQuery = new GetGroupQuery(getGameQuery.GroupUrl);
                GetGroupQueryHandlerResult getGroupQueryHandlerResult = this._getGroupQueryHandler.Execute(getGroupQuery);

                group = getGroupQueryHandlerResult.Group;

                GameFilter gameFilter = new GameFilter();
                gameFilter.WithGroupId = group.Id;
                gameFilter.WithGameNumber = getGameQuery.GameNumber;

                game = this._gameRepository.GetItems(null, null, gameFilter).Single();
            }
            else
            {
                if(getGameQuery.GameId == default(Guid))
                    this._validationState.AddError<GetGameQuery>(x => x.GameId, ErrorTypes.Empty);

                if (this._validationState.IsValid == false)
                    return result;

                GameFilter gameFilter = new GameFilter();
                gameFilter.WithId = getGameQuery.GameId;

                game = this._gameRepository.GetItems(null, null, gameFilter).Single();

                GetGroupQuery getGroupQuery = new GetGroupQuery(game.GroupId);
                GetGroupQueryHandlerResult getGroupQueryHandlerResult = this._getGroupQueryHandler.Execute(getGroupQuery);
                group = getGroupQueryHandlerResult.Group;
            }

            result.Game = game;
            result.GroupId = group.Id;
            result.GroupName = group.Name;

            BingoSetDataLoadOptions bingoSetDataLoadOptions = new BingoSetDataLoadOptions();
            bingoSetDataLoadOptions.LoadWithWordPositions = true;

            BingoSetFilter filter = new BingoSetFilter();
            filter.WithGameId = game.Id;

            IList<BingoSet> bingoSets = this._bingoSetRepository.GetItems(bingoSetDataLoadOptions, null, filter);
            
            foreach(BingoSet bingoSet in bingoSets)
            {
                CheckIfBingoSetIsFinishedQuery checkIfBingoSetIsFinishedQuery = new CheckIfBingoSetIsFinishedQuery(bingoSet);
                CheckIfBingoSetIsFinishedQueryHandlerResult checkIfBingoSetIsFinishedQueryHandlerResult =
                    this._checkIfBingoSetIsFinishedQueryHandler.Execute(checkIfBingoSetIsFinishedQuery);

                if (checkIfBingoSetIsFinishedQueryHandlerResult.IsFinished == true)
                {
                    bingoSet.IsFinished = true;
                    result.IsFinished = true;
                }
            }

            result.BingoSets = bingoSets;
            result.Succeeded = true;

            return result;
        }
    }
}